
package com.jf.cloud.product.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.jf.cloud.common.cache.constant.CacheNames;
import com.jf.cloud.common.cache.constant.LockCacheNames;
import com.jf.cloud.common.cache.constant.ProductCacheNames;
import com.jf.cloud.common.cache.util.RedisUtil;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.order.bo.RefundReductionStockBO;
import com.jf.cloud.common.product.dto.SkuDTO;
import com.jf.cloud.common.product.vo.SpuVO;
import com.jf.cloud.common.util.BooleanUtil;
import com.jf.cloud.product.bo.SkuZoneInfoBO;
import com.jf.cloud.product.mapper.SkuStockMapper;
import com.jf.cloud.product.mapper.SkuStockZoneMapper;
import com.jf.cloud.product.mapper.SpuExtensionMapper;
import com.jf.cloud.product.mapper.SpuMapper;
import com.jf.cloud.product.model.SkuStock;
import com.jf.cloud.product.service.SkuStockService;
import com.jf.cloud.product.service.SkuStockZoneService;
import com.jf.cloud.product.vo.SkuStockVO;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 库存信息
 *
 * @author zz
 * @date 2020-10-28 15:27:24
 */
@Service
public class SkuStockServiceImpl implements SkuStockService {

    @Autowired
    private SkuStockMapper skuStockMapper;

    @Autowired
    private SpuExtensionMapper spuExtensionMapper;

    @Autowired
    private SpuMapper spuMapper;

    @Autowired
    private SkuStockZoneMapper skuStockZoneMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private SkuStockZoneService skuStockZoneService;

    @Override
    public void batchSave(List<SkuStock> skuStocks) {
        skuStockMapper.batchSave(skuStocks);
    }

    @Override
    public void updateBatch(List<SkuDTO> skuList) {
        if (CollUtil.isEmpty(skuList)) {
            return;
        }
        for (SkuDTO sku : skuList) {
            if (Objects.nonNull(sku.getChangeStock()) && sku.getChangeStock() != 0) {
                RLock lock = redissonClient.getLock(LockCacheNames.LOCK_ZONE_PREFIX + sku.getSkuId());
                try {
                    lock.lock();
                    SkuStock skuStock = new SkuStock();
                    Long skuId = sku.getSkuId();
                    SkuStockServiceImpl skuStockService = (SkuStockServiceImpl) AopContext.currentProxy();
                    SkuZoneInfoBO zoneInfo = skuStockService.getZoneInfo(skuId);
                    // 如果获取到sku的分片缓存为null，就刷新下
                    if (Objects.isNull(zoneInfo)) {
                        RedisUtil.del(ProductCacheNames.SKU_ZONE_INFO + CacheNames.UNION + skuId);
                        zoneInfo = skuStockService.getZoneInfo(skuId);
                    }
                    skuStock.setStock(sku.getChangeStock());
                    skuStock.setSkuId(skuId);
                    skuStock.setSpuId(sku.getSpuId());
                    if (!BooleanUtil.isTrue(zoneInfo.getHasZone())) {
                        // 没有分片用原方式更新
                        int updateNum = skuStockMapper.updateStock(skuStock);
                        if (updateNum == 0) {
                            throw new LuckException("sku库存发生改变，请刷新后重试");
                        }
                    } else {
                        int updateNum = changeSkuWhenZone(skuStock.getSkuId(), skuStock.getStock(), skuStock.getSpuId());
                        if (updateNum == 0) {
                            throw new LuckException("sku库存发生改变，请刷新后重试");
                        }
                    }
                } finally {
                    lock.unlock();
                }
            }
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reductionStock(List<RefundReductionStockBO> refundReductionStocks) {
        for (RefundReductionStockBO refundReductionStock : refundReductionStocks) {
            Long skuId = refundReductionStock.getSkuId();
            Long spuId = refundReductionStock.getSpuId();
            RLock lock = redissonClient.getLock(LockCacheNames.LOCK_ZONE_PREFIX + skuId);
            try {
                lock.lock();
                // 获取sku是否有分片
                SkuStockServiceImpl skuStockService = (SkuStockServiceImpl) AopContext.currentProxy();
                SkuZoneInfoBO zoneInfo = skuStockService.getZoneInfo(skuId);
                // 如果获取到sku的分片缓存为null，就刷新下
                if (Objects.isNull(zoneInfo)) {
                    RedisUtil.del(ProductCacheNames.SKU_ZONE_INFO + CacheNames.UNION + skuId);
                    zoneInfo = skuStockService.getZoneInfo(skuId);
                }
                if (!BooleanUtil.isTrue(zoneInfo.getHasZone())) {
                    // 未分片按原方法
                    skuStockMapper.reductionActualStockByCancelOrder(refundReductionStock);
                } else {
                    // 已经分片
                    skuStockMapper.addStockByCancelOrder(refundReductionStock);
                }
                Integer skuZoned = skuStockService.getSkuZoned(spuId);
                if (BooleanUtil.isTrue(skuZoned)) {
                    // 商品已分片，且分片数量大于0
                    spuExtensionMapper.subSaleNumByRefund(refundReductionStock);
                } else {
                    // 未分片按原方法
                    spuExtensionMapper.reductionActualStockByRefund(refundReductionStock);
                }
            } finally {
                lock.unlock();
            }
        }
        //供应商商品id
        Map<Long, Long> supplierSpuList = refundReductionStocks.stream().filter(item -> Objects.equals(item.getSupplierProd(), 1))
                .collect(Collectors.toMap(RefundReductionStockBO::getSpuId, RefundReductionStockBO::getShopId, (v1, v2) -> v2));
        //供应商发货的代销商品信息
        if(CollUtil.isNotEmpty(supplierSpuList)){
            List<SpuVO> commissionSpuList = spuMapper.getCommissionSpuListBySupplierSpuIdAndShopId(supplierSpuList);
            if(CollUtil.isNotEmpty(commissionSpuList)){
                List<RefundReductionStockBO> commissionSpuRefundReductionStocks = new ArrayList<>();
                for(SpuVO spuVO : commissionSpuList){
                    RefundReductionStockBO commissionSpuRefundReductionStockBO = new RefundReductionStockBO();
                    RefundReductionStockBO supplierSpuRefundReductionStockBO = refundReductionStocks.stream().filter(item -> Objects.equals(item.getSpuId(), spuVO.getSupplierSpuId())).findFirst().get();
                    commissionSpuRefundReductionStockBO.setSpuId(spuVO.getSpuId());
                    commissionSpuRefundReductionStockBO.setCount(supplierSpuRefundReductionStockBO.getCount());
                    commissionSpuRefundReductionStocks.add(commissionSpuRefundReductionStockBO);
                }
                //供应商发货的商品，商家端对应代销商品减少销量
                if(CollUtil.isNotEmpty(commissionSpuRefundReductionStocks)){
                    spuExtensionMapper.reduceCommissionSpuSaleNum(commissionSpuRefundReductionStocks);
                }
            }
        }
    }


    @Override
    @Cacheable(cacheNames = CacheNames.SKU_STOCK_LIST_KEY, key = "#spuId")
    public List<SkuStockVO> listStockBySpuId(Long spuId) {
        SkuStockServiceImpl skuStockService = (SkuStockServiceImpl) AopContext.currentProxy();
        Integer skuZoned = skuStockService.getSkuZoned(spuId);
        // 商品已分片，且分片数量大于0
        if (BooleanUtil.isTrue(skuZoned)) {
            // 分片过，需要整合分片库存
            return skuStockService.listSkuStockWhichZoneBySpuId(spuId);
        }
        return skuStockMapper.listStockBySpuId(spuId);
    }

    @Override
    public List<SkuStockVO> listShopStockBySpuIdAndShopId(Long spuId, Long shopId) {
        return skuStockMapper.listShopStockBySpuIdAndShopId(spuId, shopId);
    }

    @Override
    @Cacheable(cacheNames = CacheNames.SKU_STOCK_KEY, key = "#skuId")
    public SkuStock getBySkuId(Long skuId) {
        SkuStockServiceImpl skuStockService = (SkuStockServiceImpl) AopContext.currentProxy();
        SkuZoneInfoBO zoneInfo = skuStockService.getZoneInfo(skuId);
        // 如果获取到sku的分片缓存为null，就刷新下
        if (Objects.isNull(zoneInfo)) {
            RedisUtil.del(ProductCacheNames.SKU_ZONE_INFO + CacheNames.UNION + skuId);
            zoneInfo = skuStockService.getZoneInfo(skuId);
        }

        SkuStock skuStock;
        if (!BooleanUtil.isTrue(zoneInfo.getHasZone())) {
            // 没有分片用原方式查
            skuStock = skuStockMapper.getBySkuId(skuId);
        } else {
            skuStock = skuStockMapper.getBySkuIdWhichZoned(skuId);
        }
        if (Objects.isNull(skuStock)) {
            throw new LuckException("该规格不存在或已删除，请刷新后重试");
        }
        return skuStock;
    }

    @Override
    @Cacheable(cacheNames = ProductCacheNames.SKU_ZONE_INFO, key = "#skuId", sync = true)
    public SkuZoneInfoBO getZoneInfo(Long skuId) {
        return skuStockMapper.getZoneInfo(skuId);
    }

    // 请不要在这里加缓存！！！
    @Override
    public Integer getTotalBySkuId(Long skuId) {
        // 3. 聚合扣减库存
        // 统计所有库存数量还有多少
        Integer mainStock = skuStockMapper.getStock(skuId);

        Integer sumLessZone = skuStockZoneMapper.getStockBySkuId(skuId);
        return mainStock + sumLessZone;
    }

    @Override
    public Integer getTotalBySpuId(Long spuId) {
        SkuStockServiceImpl skuStockService = (SkuStockServiceImpl) AopContext.currentProxy();
        Integer skuZoned = skuStockService.getSkuZoned(spuId);
        if (!BooleanUtil.isTrue(skuZoned)) {
            // 没有分片就按原方式获取
            return skuStockMapper.getMainStockBySpuId(spuId);
        }
        // 分片查找
        Object totalStock = RedisUtil.get(CacheNames.SPU_TOTAL_STOCK + spuId);
        if (Objects.isNull(totalStock)) {
            Integer mainStockBySpuId = skuStockMapper.getMainStockBySpuId(spuId);
            Integer totalZoneStockBySpuId = skuStockZoneMapper.getTotalZoneStockBySpuId(spuId);
            RedisUtil.set(CacheNames.SPU_TOTAL_STOCK + spuId, mainStockBySpuId + totalZoneStockBySpuId, -1);
            return totalZoneStockBySpuId + mainStockBySpuId;
        }
        return Integer.valueOf(totalStock.toString());
    }

    @Override
    @Cacheable(cacheNames = ProductCacheNames.SPU_ZONE, key = "#spuId", sync = true)
    public Integer getSkuZoned(Long spuId) {
        return skuStockZoneMapper.getSkuZoned(spuId);
    }

    @Override
    @Cacheable(cacheNames = ProductCacheNames.SPU_ZONE_STOCK, key = "#spuId", sync = true)
    public List<SkuStockVO> listSkuStockWhichZoneBySpuId(Long spuId) {
        return skuStockZoneMapper.listSkuStockWhichZoneBySpuId(spuId);
    }

    @Override
    public int changeSkuWhenZone(Long skuId, Integer changeCount, Long spuId) {
        if (changeCount > 0) {
            // 增加库存
            return skuStockMapper.updateSkuStockWhenZone(skuId, changeCount, null);
        }
        // 有分片的情况先扣主分片 主分片不够扣就全部聚合查询 扣完进行重新分片
        int updateStatus = skuStockMapper.updateSkuStockWhenZone(skuId, changeCount, null);
        if (updateStatus < 1) {
            // 主分片不够扣
            // 统计所有库存数量还有多少
            Integer mainStock = skuStockMapper.getStock(skuId);

            Integer sumLessZone = skuStockZoneMapper.getStockBySkuId(skuId);

            int skuTotalStock = mainStock + sumLessZone;
            // 库存真的不够扣减了
            if (skuTotalStock < Math.abs(changeCount)) {
                return 0;
            }
            // 剩余库存
            int lastStock = skuTotalStock - Math.abs(changeCount);
            // 重新分片
            // 库存小于200不用重新分片
            skuStockZoneService.redoZone(skuId, lastStock, spuId);
        }
        return 1;
    }

    @Override
    public List<SkuStockVO> listShopStockByPartyCodesAndShopId(List<String> partyCodes, Long shopId) {
        if (CollectionUtils.isEmpty(partyCodes)) {
            return new ArrayList<>();
        }
        return skuStockMapper.listShopStockByPartyCodesAndShopId(partyCodes, shopId);
    }
}
